package com.example.exceldemo.utils;

import cn.hutool.core.io.resource.ClassPathResource;
import com.example.exceldemo.enuns.FileType;
import com.example.exceldemo.exception.ServiceException;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static org.apache.commons.io.IOUtils.toByteArray;

/**
 * @Description 导出文件工具类
 * @Author xiaohuang
 * @Date 2024/9/11
 */
public class FileUtils {
    private static final Logger log = LoggerFactory.getLogger(PdfUtil.class);

    public static void  exportByte(byte[] wordData , String fileName, FileType fileType){
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        try {
            // 设置响应头
            response.setContentType(fileType.getDesc());
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8")+fileType.getType());
            //设置字符编码
            response.setCharacterEncoding("UTF-8");
            OutputStream out = response.getOutputStream();
            out.write(wordData);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("生成word异常");
        }

    }

    /**
     * 设置HTTP响应的内容类型和编码方式
     * 此方法用于根据文件类型和文件名设置HTTP响应的编码格式、内容类型以及下载时的文件名
     *
     * @param response HTTP响应对象，用于设置响应头和编码信息
     * @param fileName 下载时显示的文件名
     * @param fileType 文件类型枚举，包含文件类型描述和文件扩展名
     */
    public static void setContentType(HttpServletResponse response, String fileName, FileType fileType) {
        try {
            // 设置响应内容的编码格式为UTF-8
            response.setCharacterEncoding("UTF-8");
            // 根据文件类型设置响应的内容类型
            response.setContentType(fileType.getDesc());
            // 设置响应头，指定文件下载时的文件名和文件类型
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8") + fileType.getType());
        } catch (UnsupportedEncodingException e) {
            // 当文件名编码失败时，抛出自定义异常
            throw new ServiceException("设置ContentType失败: " + e.getMessage());
        }
    }
    //优化，判断他是docx，还是pdf、excel导出
    public static void exportXwpfDocument(XWPFDocument xwpfDocument, String fileName, FileType fileType){
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        setContentType(response, fileName, fileType);
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            xwpfDocument.write(outputStream);
        } catch (IOException e) {
            throw new ServiceException("导出文件失败：" + e.getMessage());
        }finally {
            try {
                if (xwpfDocument != null) {
                    xwpfDocument.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 获取文件绝对路径的字节数组
     * @param realPath
     */
    public static byte[] getByte(String realPath) {
        try (FileInputStream inputStream = new FileInputStream(realPath)) {
            if (ObjectUtils.isEmpty(inputStream)) {
                throw new ServiceException("获取照片失败！文件路径无效：" + realPath);
            }
            byte[] bytes = toByteArray(inputStream);
            return bytes;
        } catch (FileNotFoundException e) {
            throw new ServiceException("图片文件不存在：" + e.getMessage());
        } catch (IOException e) {
            throw new ServiceException("读取图片失败：" + e.getMessage());
        }
    }
    /**
     * 根据类路径加载资源并将其转换为字节数组
     * 此方法用于从类路径中加载资源，例如配置文件或模板文件，并将其内容转换为字节数组
     *
     * @param classPath 类路径资源的路径
     * @return 资源文件的字节数组表示
     */
    public static byte[] byteByClassPath(String classPath) {
        // 创建 ClassPathResource 对象以访问类路径资源
        ClassPathResource resource = new ClassPathResource(classPath);
        try (InputStream inputStream = resource.getStream()) {
            // 调用辅助方法将输入流转换为字节数组
            return toByteArray(inputStream);
        } catch (IOException e) {
            // 捕获 IOException 并抛出自定义异常，提供更详细的错误信息
            throw new ServiceException("Failed to read resource from classpath: " + classPath, e);
        }
    }
 /**
  * 根据类路径或实际路径获取输入流
  * 该方法用于根据提供的类路径或实际文件路径，返回对应的输入流，以便读取文件内容
  * 如果提供的实际路径为空，则认为需要从类路径资源中获取文件，否则从实际路径中获取
  *
  * @param classPath 类路径，用于在类路径资源中定位文件
  * @param realPath 实际文件路径，如果为空，则使用类路径参数
  * @return InputStream 返回一个输入流，用于读取文件内容
  */
 public static InputStream getInputStream(String classPath, String realPath) {
     try {
         // 判断实际路径是否为空，如果为空，则从类路径资源中获取文件
         if (ObjectUtils.isEmpty(realPath)) {
             ClassPathResource resource = new ClassPathResource(classPath);
             return resource.getStream();
         } else {
             // 如果实际路径不为空，则直接从实际路径中获取文件
             return new FileInputStream(realPath);
         }
     } catch (FileNotFoundException e) {
         // 如果文件不存在，则抛出自定义的服务异常
         throw new ServiceException("图片文件不存在：" + e.getMessage());
     }
 }

    public static void exportWorkbookExcel(HttpServletResponse response, Workbook workbook, String fileName,FileType fileType) {
        // 验证文件名是否合法，防止XSS攻击
        ServletOutputStream out =null;
        try {// 设置响应头信息
            out = response.getOutputStream();
            response.setCharacterEncoding("UTF-8");
            response.setContentType(fileType.getDesc());
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8") + fileType.getType());
            // 导出Excel
            workbook.write(out);
        } catch (IOException e) {
            // 异常处理
            e.printStackTrace();
            // 可以在这里记录日志或者返回错误信息给调用者
            throw new ServiceException("导出异常："+ e.getMessage());
        }finally {
            try {
                if (workbook != null) {
                    workbook.close();
                }
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 把多个文件压缩成zip文件
     * @param documentBytes
     * @param fileName
     * @param response
     * @param fileType
     */
    public static void addFileToZip(Map<String, byte[]> documentBytes, String fileName, HttpServletResponse response, String fileType) {
        if (documentBytes.isEmpty())
            throw new ServiceException(fileType+"map集合为空");
        try {
            // 设置响应头
            response.setContentType("application/zip");
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8")+".zip");
            // 获取响应的输出流
            ServletOutputStream out = response.getOutputStream();
            try (ZipOutputStream zipOutputStream = new ZipOutputStream(out)) {
                // 循环遍历pdf并将它们添加到ZIP文件中
                for (Map.Entry<String, byte[]> entry : documentBytes.entrySet()) {
                    String pdfName = entry.getKey();
                    byte[] pdfContent = entry.getValue();
                    // 为每个PDF创建一个ZIP条目
                    zipOutputStream.putNextEntry(new ZipEntry(pdfName + "."+fileType));
                    log.info("开始将"+fileType+"内容写入Zip.......");
                    // 将PDF内容写入ZIP文件
                    zipOutputStream.write(pdfContent);
                    log.info("写入Zip结束........");
                    zipOutputStream.closeEntry();
                }
            }
            // 关闭响应输出流
            out.flush();
            out.close();
        } catch (IOException e) {
            throw new ServiceException("生成zip异常");
        }
    }

}
